import logging

from configs import web_config

logger = logging.getLogger(__name__)


class LocalStorage:
    """
    本地存储类，采用分离式结构：
        category_to_codes: {category_id: [code, code, ...], ...}
        code_to_data: {code: data_dict, ...}

    实现类别到编码、编码到数据的映射。
    """

    def __init__(self):
        self._category_to_codes = {}  # 类别ID -> 编码列表（保持顺序）
        self._code_to_data = {}  # 编码 -> 数据字典

    def add_dict(self, category_id, code, data_dict):
        """
        向指定类别添加一个编码，并绑定一个数据字典。

        Args:
            category_id: 类别ID。
            code: 唯一编码（全局唯一）。
            data_dict: 要存储的字典。

        Raises:
            TypeError: data_dict 不是字典。
            ValueError: code 已存在（避免冲突）。
        """
        if not isinstance(data_dict, dict):
            raise TypeError("data_dict 必须是一个字典")

        if code in self._code_to_data:
            raise ValueError(f"编码 '{code}' 已存在，不能重复添加。")

        # 确保类别存在
        if category_id not in self._category_to_codes:
            self._category_to_codes[category_id] = []

        # 添加编码到类别，并存储数据
        self._category_to_codes[category_id].append(code)
        self._code_to_data[code] = data_dict.copy()  # 存副本
        if web_config.DEBUG:
            logger.info(f"已将编码 '{code}' 添加到类别 '{category_id}'，对应数据: {data_dict}")

    def remove_by_code(self, code):
        """
        根据编码删除该条目（从所有类别中移除，并删除数据）。

        Args:
            code: 要删除的编码。

        Raises:
            KeyError: 编码不存在。
        """
        if code not in self._code_to_data:
            raise KeyError(f"编码 '{code}' 不存在。")

        # 从所有类别中移除该 code（理论上一个 code 只属于一个 category，但这里做全量清理）
        for codes in self._category_to_codes.values():
            if code in codes:
                codes.remove(code)

        # 删除数据
        removed_data = self._code_to_data.pop(code)
        if web_config.DEBUG:
            logger.info(f"已删除编码 '{code}'，对应数据: {removed_data}")
        return removed_data

    def remove_by_category(self, category_id):
        """
        删除整个类别及其所有关联的编码和数据。

        Args:
            category_id: 类别ID。

        Raises:
            KeyError: 类别不存在。
        """
        if category_id not in self._category_to_codes:
            raise KeyError(f"类别ID '{category_id}' 不存在。")

        codes = self._category_to_codes[category_id]
        for code in codes:
            self._code_to_data.pop(code, None)

        del self._category_to_codes[category_id]
        if web_config.DEBUG:
            logger.info(f"已删除类别 '{category_id}' 及其 {len(codes)} 个编码。")

    def remove_by_field(self, category_id, field_name, field_value):
        """
        删除类别中字段 field_name == field_value 的所有字典（通过编码删除）。

        Args:
            category_id: 类别ID。
            field_name: 字段名。
            field_value: 字段值。
        """
        if category_id not in self._category_to_codes:
            raise KeyError(f"类别ID '{category_id}' 不存在。")

        codes = self._category_to_codes[category_id]
        removed_codes = []
        removed_dicts = []

        for code in codes[:]:  # 复制列表用于迭代
            data = self._code_to_data.get(code)
            if data and field_name in data and data[field_name] == field_value:
                # 删除该编码
                self._code_to_data.pop(code)
                codes.remove(code)
                removed_codes.append(code)
                removed_dicts.append(data)

        if not removed_codes:
            raise KeyError(f"在类别 '{category_id}' 中未找到字段 '{field_name}' 等于 '{field_value}' 的条目。")
        if web_config.DEBUG:
            logger.info(f"已删除 {len(removed_codes)} 个匹配的条目，编码: {removed_codes}")
        return removed_dicts

    def get_dicts(self, category_id):
        """
        获取指定类别下的所有数据字典（按顺序）。

        Args:
            category_id: 类别ID。

        Returns:
            list: 数据字典副本列表。类别不存在则返回空列表。
        """
        codes = self._category_to_codes.get(category_id, [])
        return [self._code_to_data[code].copy() for code in codes if code in self._code_to_data]

    def get_dict_by_code(self, code):
        """
        根据编码获取对应的数据字典。

        Args:
            code: 编码。

        Returns:
            dict 或 None：存在则返回副本，否则返回 None。
        """
        data = self._code_to_data.get(code)
        return data.copy() if data else None

    def get_codes(self, category_id):
        """
        获取类别下的所有编码。

        Args:
            category_id: 类别ID。

        Returns:
            list: 编码列表。不存在则返回空列表。
        """
        return self._category_to_codes.get(category_id, []).copy()

    def exists_category(self, category_id):
        """类别是否存在"""
        return category_id in self._category_to_codes

    def exists_code(self, code):
        """编码是否存在"""
        return code in self._code_to_data

    def has_dicts(self, category_id):
        """类别是否存在且有数据"""
        return self.exists_category(category_id) and len(self._category_to_codes[category_id]) > 0

    def list_all_categories(self):
        """
        获取所有类别及其编码列表。

        Returns:
            dict: {category_id: [code, ...]}
        """
        return {k: v.copy() for k, v in self._category_to_codes.items()}

    def list_all_data(self):
        """
        获取所有编码对应的数据（副本）。

        Returns:
            dict: {code: data_dict_copy, ...}
        """
        return {k: v.copy() for k, v in self._code_to_data.items()}

    def clear(self):
        """清空所有数据"""
        self._category_to_codes.clear()
        self._code_to_data.clear()
        if web_config.DEBUG:
            logger.debug("所有数据已清空。")

    def size(self):
        """总数据条目数（编码总数）"""
        return len(self._code_to_data)

    def category_count(self):
        """类别数量"""
        return len(self._category_to_codes)


rule_storage = LocalStorage()